package com.blue.encryption;

import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.UnsupportedEncodingException;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * @Description: RSA是公钥私钥加密，一种典型的非对称加密
 *      该算法基于一个十分简单的数论事实：将两个大素数相乘十分容易，但那时想要对其乘积进行因式分解却极其困难，
 *      因此可以将乘积公开作为加密密钥，即公钥，而两个大素数组合成私钥。
 *      公钥是可发布的供任何人使用，私钥则为自己所有，供解密之用。
 *
 *        算法实现过程为：
 *         1. 随意选择两个大的质数p和q，p不等于q，计算N=pq。
 *         2. 根据欧拉函数，不大于N且与N互质的整数個数為(p-1)(q-1)。
 *         3. 选择一个整数e与(p-1)(q-1)互质，并且e小于(p-1)(q-1)。
 *         4. 用以下这个公式计算d：d× e ≡ 1 (mod (p-1)(q-1))。
 *         5. 将p和q的记录销毁。
 *
 *  参考资料https://www.cnblogs.com/AloneSword/p/3326750.html
 * @Auther: lbb
 * @Date: 2019/1/22 16:38
 */
public class RSAUtil {
    public static final String ALGORITHM_NAME="RSA";
    public static final int KEY_TYPE_PRIVATE=1;
    public static final int KEY_TYPE_PUBLIC=2;

    /**
     * 方法 1.KeyPairGenerator生成密钥对
     *      2.使用keytool生成jks文件
     *      3.使用openssl生成密钥对
     * @return
     * @throws NoSuchAlgorithmException
     */
    public static KeyPair generateKeyPair()  {
        KeyPairGenerator keyPairGenerator= null;
        KeyPair keyPair=null;
        try {
            //获取密钥对生成器
            keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM_NAME);
            //获取密钥对
            keyPair=keyPairGenerator.generateKeyPair();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return keyPair;
    }

    /**
     * 数据加密
     * 公钥加密私钥解密/私钥加密公钥解密
     * @param content 字符串
     * @param key 公钥/私钥
     * @return
     */
    public static String encrypt(String content,Key key){
        byte[] bytes= new byte[0];
        try {
            bytes = rsaCrypt(content.getBytes("UTF-8"),key, Cipher.ENCRYPT_MODE);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
//        将加密结果转位Hex
        return Hex.encodeHexString(bytes);
    }

    /**
     *数据解密
     * 公钥加密私钥解密/私钥加密公钥解密
     * @param content 加密后的HexStr
     * @param key 公钥/私钥
     * @return
     */
    public static String decrypt(String content,Key key){
        byte[] bytes=null;
        String str="";
        try {
            byte[] cryptStr=Hex.decodeHex(content.toCharArray());
            bytes=rsaCrypt(cryptStr,key,Cipher.DECRYPT_MODE);
            str=new String(bytes,"UTF-8");
        } catch (DecoderException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return str;
    }

    /**
     * 密码机封装
     * @param content
     * @param key
     * @param model
     * @return
     */
    public static byte[] rsaCrypt(byte[] content, Key key,int model){
        byte [] bytes=null;
        try {
            Cipher cipher=Cipher.getInstance(ALGORITHM_NAME);
            cipher.init(model,key);
            bytes=cipher.doFinal(content);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        }
        return bytes;
    }

    /**
     * 加密
     * @param content 内容
     * @param keyStr 密钥字符串
     * @return
     */
    public static String encrypt(String content,String keyStr,int keyType){
        Key key=null;
        try {
            byte[] bytes=Hex.decodeHex(keyStr.toCharArray());
            switch (keyType){
                case KEY_TYPE_PRIVATE:
                    key=getPrivateKey(bytes);
                    break;
                case KEY_TYPE_PUBLIC:
                    key=getPublicKey(bytes);
                    break;
                default:
                        return null;
            }
        } catch (DecoderException e) {
            e.printStackTrace();
        }
        return encrypt(content,key);
    }

    /**
     * 解密
     * @param content 内容
     * @param keyStr 密钥字符串
     * @return
     */
    public static String decrypt(String content,String keyStr,int keyType){
        Key key=null;
        try {
            byte[] bytes=Hex.decodeHex(keyStr.toCharArray());
            switch (keyType){
                case KEY_TYPE_PRIVATE:
                    key=getPrivateKey(bytes);
                    break;
                case KEY_TYPE_PUBLIC:
                    key=getPublicKey(bytes);
                    break;
                default:
                    return null;
            }
        } catch (DecoderException e) {
            e.printStackTrace();
        }
        return decrypt(content,key);
    }

    /**
     * 将公钥的字节PublicKey,可以是密钥字符串
     * 或通过KeyStore keyStore读取jks中的密钥对，
     * 参考SignatureUtil读取per文件中的公钥密钥的方法获取per中的密钥对
     * @param keyBytes
     * @return
     */
    private static PublicKey getPublicKey(byte[] keyBytes){
        PublicKey publicKey=null;
        try {
            X509EncodedKeySpec keySpec=new X509EncodedKeySpec(keyBytes);
            KeyFactory keyFactory=KeyFactory.getInstance(ALGORITHM_NAME);
            publicKey=keyFactory.generatePublic(keySpec);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
        }
        return publicKey;
    }

    /**
     *
     * @param keyBytes
     * @return
     */
    private static PrivateKey getPrivateKey(byte[] keyBytes){
        PrivateKey privateKey=null;
        try {
            PKCS8EncodedKeySpec keySpec=new PKCS8EncodedKeySpec(keyBytes);
            KeyFactory keyFactory=KeyFactory.getInstance(ALGORITHM_NAME);
            privateKey=keyFactory.generatePrivate(keySpec);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
        }
        return privateKey;
    }
}
